/**
 * Copyright (c) 2006, Mark Allen. All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 * 
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 * 
 * Neither the name of Planet Achewood nor the names of its contributors may be
 * used to endorse or promote products derived from this software without
 * specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 * 	
 *	@author Mark Allen [mark.a.allen@gmail.com]
 *	@since Oct 13, 2006: $Revision: 127 $ $Date: 2006-12-23 23:45:09 +0000 (Sat, 23 Dec 2006) $
 */
 
function removeAllChildren(element) {
	while(element.firstChild) {
		element.removeChild(element.firstChild);
	}
}

function buildSmallHorizontalSpacer() {
	var spacer = document.createElement("span");
	spacer.setAttribute("class", "small-horizontal-spacer");
	spacer.setAttribute("className", "small-horizontal-spacer");
	
	return spacer;
}

function trim(string) {
	return string.replace(/^\s+/g, "").replace(/\s+$/g, "");
}

function appendStringWithNewlines(string, element) {
	var splitString = string.split("\n");

	for(var j = 0; j < splitString.length; j++) {
		splitString[j] = trim(splitString[j]);
		if(splitString[j].length > 0) {
			element.appendChild(document.createTextNode(splitString[j]));
			if((j+1) != splitString.length) {
				element.appendChild(document.createElement("br"));
			}
		}
	}
}

function appendTranscript(string, element) {
	var splitString = string.split("\n");

	for(var j = 0; j < splitString.length; j++) {
		splitString[j] = trim(splitString[j]);
		if(splitString[j].length > 0) {
			splitString[j] = trim(splitString[j]);
			
			var elementToDisplay;
			var characterNameIndex = splitString[j].indexOf(":");
			
		 	if(splitString[j].indexOf("[[") == 0) {
				var narrationSpan = document.createElement("span");
				narrationSpan.setAttribute("class", "transcript-narration");				
				narrationSpan.setAttribute("className", "transcript-narration");
				splitString[j] = splitString[j].replace("[[", "(");
				splitString[j] = splitString[j].replace("]]", ")");
				narrationSpan.appendChild(document.createTextNode(splitString[j]));
				elementToDisplay = narrationSpan;
			} else if(splitString[j].indexOf("<<") == 0) {
				var soundEffectSpan = document.createElement("span");
				soundEffectSpan.setAttribute("class", "transcript-sound-effect");				
				soundEffectSpan.setAttribute("className", "transcript-sound-effect");
				splitString[j] = splitString[j].replace("<<", "");
				splitString[j] = splitString[j].replace(">>", "");				
				soundEffectSpan.appendChild(document.createTextNode(splitString[j]));
				elementToDisplay = soundEffectSpan;
			} else if(splitString[j].indexOf("{{") == 0) {
				var altTextSpan = document.createElement("span");
				altTextSpan.setAttribute("class", "transcript-alt-text");				
				altTextSpan.setAttribute("className", "transcript-alt-text");	
				splitString[j] = splitString[j].replace("{{", "[");
				splitString[j] = splitString[j].replace("}}", "]");							
				altTextSpan.appendChild(document.createTextNode(splitString[j]));			
				elementToDisplay = altTextSpan;				
			} else if(characterNameIndex != -1) {
				var characterNameSpan = document.createElement("span");
				characterNameSpan.setAttribute("class", "transcript-character-name");				
				characterNameSpan.setAttribute("className", "transcript-character-name");
				characterNameSpan.appendChild(document.createTextNode(splitString[j].substring(0, characterNameIndex)));
				element.appendChild(characterNameSpan);
				elementToDisplay = document.createTextNode(splitString[j].substring(characterNameIndex));
			} else {
				elementToDisplay = document.createTextNode(splitString[j]);
			}
			
			if(typeof elementToDisplay != "undefined") {
				element.appendChild(elementToDisplay);
			}

			if((j+1) != splitString.length) {
				element.appendChild(document.createElement("br"));
			}
		}
	}
}

var RevetknUtils = {
	
	apply: function(proc, args) {
		return proc.apply(proc, args);
	},
	
	removeAllChildren: function(element) {
		while(element.firstChild) {
			element.removeChild(element.firstChild);
		}
	}	
};

/**
 * Creates a pager over pageable data.
 */
function Pager(pageRetrievalFunction, pageDisplayFunction,	totalResultsFunction, pagingDisplayElementId, extraProperties) {
	this.PAGE_SIZE = 10;
	this.SELECTED_PAGE_RADIX = this.PAGE_SIZE / 2;
	this.pageRetrievalFunction = pageRetrievalFunction;
	this.pageDisplayFunction = pageDisplayFunction;
	this.totalResultsFunction = totalResultsFunction;
	this.pagingDisplayElementId = pagingDisplayElementId;
	this.additionalArgs = (typeof extraProperties == "undefined") ? undefined : extraProperties.additionalArgs;
	this.onTotalResultsRetrieved = (typeof extraProperties == "undefined") ? undefined : extraProperties.onTotalResultsRetrieved;
	this.currentIndex = 0;
	this.totalResults = 0;	
	var that = this;
	
	this.retrieveAndShowPage = function() {
		var totalPages = Math.ceil(that.totalResults / that.PAGE_SIZE);
		var currentPage = (that.currentIndex / that.PAGE_SIZE) + 1;
		var resultsUpperBound = that.currentIndex + that.PAGE_SIZE > that.totalResults ? that.totalResults : that.currentIndex + that.PAGE_SIZE;

		RevetknUtils.removeAllChildren($(that.pagingDisplayElementId));
		
		// Pull in page data
		if(typeof that.additionalArgs == "undefined") {
			that.pageRetrievalFunction(that.currentIndex, that.pageDisplayFunction);
		} else {
			var args = [];
			for(var i = 0; i < that.additionalArgs.length; i++) {
				args.push(that.additionalArgs[i]);
			}
			args.push(that.currentIndex);
			args.push(that.pageDisplayFunction);
			RevetknUtils.apply(that.pageRetrievalFunction, args);
		}
		
		// Bail if there's nothing to display
		if(that.totalResults == 0) {
			return;
		}		
		
		var pageContainer = document.createElement("span");
		pageContainer.setAttribute("class", "pager-page-container");
		pageContainer.setAttribute("className", "pager-page-container");
		
		var previousButton = document.createElement("a");
		previousButton.setAttribute("class", "pager-page");
		previousButton.setAttribute("className", "pager-page");		
		previousButton.appendChild(document.createTextNode("< Previous"));
		previousButton.onclick = function() {
			that.previous();
		}

		if(that.currentIndex != 0) {
			pageContainer.appendChild(previousButton);
		}
		
		var pageStart = currentPage - this.SELECTED_PAGE_RADIX < 1 ? 1 : currentPage - this.SELECTED_PAGE_RADIX;
		var pageEnd = currentPage + this.SELECTED_PAGE_RADIX > totalPages ? totalPages : currentPage + this.SELECTED_PAGE_RADIX;
		
		var createPageLink = function(pageNumber) {
			var pageLink = document.createElement("a");
			var pageLinkClass = (currentPage == i ? "pager-active-page" : "pager-page");
			pageLink.setAttribute("name", pageNumber.toString());
			pageLink.setAttribute("class", pageLinkClass);						
			pageLink.setAttribute("className", pageLinkClass);
			pageLink.setAttribute("href", "javascript://");		
			pageLink.onclick = function() {
				that.displayPage(this.getAttribute("name"));
			}	
			
			pageLink.appendChild(document.createTextNode(pageNumber.toString()));
			
			return pageLink;
		};
		
		if(pageStart != 1) {
			pageContainer.appendChild(createPageLink(1));
			pageContainer.appendChild(document.createTextNode(" ... "));
		}
		
		for(var i = pageStart; i <= pageEnd; i++) {
			// Only show pages when there is more than one page
			if(pageStart != pageEnd) {
				pageContainer.appendChild(createPageLink(i));
			}
		}
		
		if(pageEnd != totalPages) {
			pageContainer.appendChild(document.createTextNode(" ... "));			
			pageContainer.appendChild(createPageLink(totalPages));			
		}			
		
		
		$(that.pagingDisplayElementId).appendChild(pageContainer);
		
		var nextButton = document.createElement("a");
		nextButton.setAttribute("class", "pager-page");
		nextButton.setAttribute("className", "pager-page");		
		nextButton.appendChild(document.createTextNode("Next >"));
		nextButton.onclick = function() {
			that.next();
		}
		
		if(resultsUpperBound != that.totalResults) {
			pageContainer.appendChild(nextButton);	
		}	
	}
	
	this.next = function() {
		// Can only go next if there are more pages to display
		if(that.currentIndex + that.PAGE_SIZE < that.totalResults) {
			that.currentIndex += that.PAGE_SIZE;
		}
		
		that.retrieveAndShowPage();
	}
	
	this.previous = function() {
		if(that.currentIndex - that.PAGE_SIZE < 0) {
			that.currentIndex = 0;
		} else {
			that.currentIndex -= that.PAGE_SIZE;
		}
		
		that.retrieveAndShowPage();
	}
	
	this.totalResultsCallback = function(totalResults) {
		// Let clients know about the total results if they're interested
		if(typeof that.onTotalResultsRetrieved != "undefined") {
			that.onTotalResultsRetrieved(totalResults);	
		}
		
		that.totalResults = totalResults;
		that.retrieveAndShowPage();
	}
	
	this.displayPage = function(pageNumber) {
		that.currentIndex = (pageNumber - 1) * that.PAGE_SIZE;
		that.retrieveAndShowPage();
	}
	
	// Once we know how many total results there are, show the first page immediately
	if(typeof that.additionalArgs == "undefined") {
		totalResultsFunction(that.totalResultsCallback);		
	} else {
		var args = [];
		for(var i = 0; i < that.additionalArgs.length; i++) {
			args.push(that.additionalArgs[i]);
		}
		args.push(that.totalResultsCallback);
		RevetknUtils.apply(that.totalResultsFunction, args);		
	}	
}